#pragma once

#ifndef _VOXELSPACE_H_
#define _VOXELSPACE_H_

#include "BBox.h"
#include <maya/MVector.h>
#include <maya/MFloatPoint.h>
#include <vector>
#include "Visualizer.h"

typedef struct {
	MFloatPoint p1;
	MFloatPoint p2;
	MFloatPoint p3;
} triangle;

typedef struct {
	triangle t;
	int object;
	int index;
} triangle_object;

// statistics for closest intersection
struct CIStats
{
  CIStats()
    : found_poly_intersections(0)
    , total_voxels_traversed(0)
    , total_ray_poly_tests(0)
  {}
  CIStats& operator+=(const CIStats& rhs){
    found_poly_intersections += rhs.found_poly_intersections;
    total_voxels_traversed += rhs.total_voxels_traversed;
    total_ray_poly_tests += rhs.total_ray_poly_tests;
    return *this;
  }
  int found_poly_intersections;
  int total_voxels_traversed;
  int total_ray_poly_tests;
};


class VoxelSpace
{
public:
  VoxelSpace();
  ~VoxelSpace(void);

  MVector center() const;
  void get_voxel_coord(const MFloatPoint& space_coord, Idx3& vox_coord) const;
  int get_voxel_idx(const Idx3& vox_coord) const;
  Idx3 idx_3d_from_idx(int idx) const;
  void Init(const BBox& box, int voxel_factor);
  bool AssignTrianglesToVoxels(std::vector<std::vector<triangle>> triangles);
  void Bresenham3D(const Idx3& p0, const Idx3& p1, std::vector<Idx3>& points);
  bool RayCubeIntersection(const MVector& dir, const MFloatPoint& p0,float& tnear,float& tfar) const;
  void floatAlgo(const MFloatPoint& point1, const MFloatPoint& point2, const MVector& ray_, 
    std::vector<Idx3>& points, std::vector<int>& indices,  
    MFloatVector& ray_scaled, MFloatPoint& pt1_scaled, MFloatPoint& pt2_scaled) const;
  bool ourClosestIntersection(const MVector& rayDir, const MFloatPoint& raySource, 
    float& minHitRayParam, triangle_object& min_tri, MFloatPoint& minHitPoint, 
    float& Bary1, float& Bary2, float& Bary3,
    CIStats& ci_stats_,
    triangle_object* skip_tri=NULL) const;
  bool intersectsTriangle(const MFloatPoint& p0, const MVector& r, 
    const MFloatPoint& p1, const MFloatPoint& p2, const MFloatPoint& p3, 
    MFloatPoint& p, float& mult) const;
  float getDiagLength() const { return m_diagLength; }
  const BBox& bounding_box() const { return m_bbox; }
  const MVector& inc_vec() const { return m_inc; }

  std::vector<std::vector<triangle_object>> m_tri_inside_vox;
  int m_vfactor;
  mutable int m_total_voxels_for_all_rays;
  mutable int m_total_polygons_for_all_rays;
  mutable int m_found_intersections_with_polygons;

private:
  bool m_bInit;
  bool m_bTrisAssigned;
  BBox m_bbox;
  MVector m_inc;
  float m_diagLength;
};

#endif